﻿using System.Collections;
using System.DirectoryServices;
using System.Security.AccessControl;
using System.Security.Principal;
using System.Collections.Generic;

namespace ActiveDirectoryUtilities.CheckDsAcls
{
	public class ActiveDirectoryAcl : IEnumerable, IEnumerator
	{
		private string _distinguishedName, _objectClass;
		private ActiveDirectorySecurity _ads = new ActiveDirectorySecurity(), _comparisonAds = null;
		private IdentityReference _ownerAccount;
		private IdentityReference _ownerGroup;
		private bool _showDacls, _showSacls;
		private AuthorizationRuleCollection _dacl, _sacl;
		private int _currentDaclAce = -1, _currentSaclAce = -1, _currentMissingAce = -1;
		List<AnalyzableAce> _missingAces = new List<AnalyzableAce>();

		internal ActiveDirectoryAcl(string distinguishedName, string objectClass, byte[] securityDescriptor, bool showDacls, bool showSacls)
		{
			_distinguishedName = distinguishedName;
			_objectClass = objectClass;
			if (securityDescriptor != null)
			{
				_ads.SetSecurityDescriptorBinaryForm(securityDescriptor);
			}
			_showDacls = showDacls;
			_showSacls = showSacls;
			internalSetup();
		}

		internal ActiveDirectoryAcl(string distinguishedName, string objectClass, string securityDescriptor, bool showDacls, bool showSacls)
		{
			_distinguishedName = distinguishedName;
			_objectClass = objectClass;
			if (!string.IsNullOrEmpty(securityDescriptor))
			{
				_ads.SetSecurityDescriptorSddlForm(securityDescriptor);
			}
			_showDacls = showDacls;
			_showSacls = showSacls;
			internalSetup();
		}

		private void internalSetup()
		{
			try
			{
				_ownerAccount = _ads.GetOwner(typeof(NTAccount));
			}
			catch (IdentityNotMappedException)
			{
				_ownerAccount = _ads.GetOwner(typeof(SecurityIdentifier));
			}

			try
			{
				_ownerGroup = _ads.GetGroup(typeof(NTAccount));
			}
			catch (IdentityNotMappedException)
			{
				_ownerGroup = _ads.GetGroup(typeof(SecurityIdentifier));
			}

			_dacl = _ads.GetAccessRules(true, true, typeof(NTAccount));
			_sacl = _ads.GetAuditRules(true, true, typeof(NTAccount));
		}

		internal void CompareToAcl(string securityDescriptorToCompareAgainst)
		{
			if (!string.IsNullOrEmpty(securityDescriptorToCompareAgainst))
			{
				_comparisonAds = new ActiveDirectorySecurity();
				_comparisonAds.SetSecurityDescriptorSddlForm(securityDescriptorToCompareAgainst);
				findMissingAces();
			}
		}
		internal void CompareToAcl(byte[] securityDescriptorToCompareAgainst)
		{
			if (securityDescriptorToCompareAgainst != null)
			{
				_comparisonAds = new ActiveDirectorySecurity();
				_comparisonAds.SetSecurityDescriptorBinaryForm(securityDescriptorToCompareAgainst);
				findMissingAces();
			}
		}

		private void findMissingAces()
		{
			if (_showDacls)
			{
				foreach (ActiveDirectoryAccessRule dacl in _comparisonAds.GetAccessRules(true, true, typeof(NTAccount)))
				{
					AnalyzableAce defaultAce = new AnalyzableAce(dacl, _ads.AreAccessRulesProtected, _ads.AreAuditRulesProtected);
					defaultAce.LookForAceInSecurityDescriptor(_ads, _ownerAccount, true);
					if (defaultAce.IsAceInSecurityDescriptor == AceRelationToReferenceSecurityDescriptor.Missing)
					{
						_missingAces.Add(defaultAce);
					}
				}
			}

			if (_showSacls)
			{
				foreach (ActiveDirectoryAuditRule sacl in _comparisonAds.GetAuditRules(true, true, typeof(NTAccount)))
				{
					AnalyzableAce defaultAce = new AnalyzableAce(sacl, _ads.AreAccessRulesProtected, _ads.AreAuditRulesProtected);
					defaultAce.LookForAceInSecurityDescriptor(_ads, _ownerAccount, true);
					if (defaultAce.IsAceInSecurityDescriptor == AceRelationToReferenceSecurityDescriptor.Missing)
					{
						_missingAces.Add(defaultAce);
					}
				}
			}
		}

		#region Properties
		public int CountAces { get { return _dacl.Count + _sacl.Count; } }
		public int SizeOfSecurityDescriptor { get { return _ads.GetSecurityDescriptorBinaryForm().Length; } }
		public string DistinguishedName { get { return _distinguishedName; } }
		public IdentityReference OwnerAccount { get { return _ownerAccount; } }
		public IdentityReference OwnerGroup { get { return _ownerGroup; } }
		public bool AreAccessRulesProtected { get { return _ads.AreAccessRulesProtected; } }
		public bool AreAccessRulesCanonical { get { return _ads.AreAccessRulesCanonical; } }
		public string ObjectClass { get { return _objectClass; } }
		#endregion

		#region IEnumerable Members

		public IEnumerator GetEnumerator()
		{
			return this;
		}

		#endregion

		#region IEnumerator Members

		public object Current
		{
			get
			{
				if ((_currentDaclAce < _dacl.Count) && (_showDacls))
				{
					AnalyzableAce ace = new AnalyzableAce(_dacl[_currentDaclAce], _ads.AreAccessRulesProtected, _ads.AreAccessRulesCanonical);
					ace.LookForAceInSecurityDescriptor(_comparisonAds, _ownerAccount);
					return ace;
				}
				else if ((_currentSaclAce < _sacl.Count) && (_showSacls))
				{
					AnalyzableAce ace = new AnalyzableAce(_sacl[_currentSaclAce], _ads.AreAuditRulesProtected, _ads.AreAuditRulesCanonical);
					ace.LookForAceInSecurityDescriptor(_comparisonAds, _ownerAccount);
					return ace;
				}
				else if (_currentMissingAce < _missingAces.Count)
				{
					return _missingAces[_currentMissingAce];
				}
				return null;
			}
		}

		public bool MoveNext()
		{
			if ((++_currentDaclAce < _dacl.Count) && (_showDacls))
			{
				return true;
			}
			else if ((++_currentSaclAce < _sacl.Count) && (_showSacls))
			{
				return true;
			}
			else if ((++_currentMissingAce < _missingAces.Count))
			{
				return true;
			}

			return false;
		}

		public void Reset()
		{
			_currentDaclAce = -1;
			_currentSaclAce = -1;
			_currentMissingAce = -1;
		}

		#endregion
	}
}
